/*
 * Copyright (c) 2023-2025 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.mybatis.ext.mybatis.extension;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.apache.commons.compress.utils.Lists;
import org.elsfs.cloud.common.mybatis.ext.sql.AbstractCondition;
import org.elsfs.cloud.common.mybatis.ext.sql.CompositeSqlFragment;
import org.elsfs.cloud.common.mybatis.ext.sql.JoinConditionBuilder;
import org.elsfs.cloud.common.mybatis.ext.sql.JoinType;
import org.elsfs.cloud.common.mybatis.ext.sql.SelectJoinBuilder;
import org.elsfs.cloud.common.mybatis.ext.sql.SqlParameterManager;
import org.elsfs.cloud.common.mybatis.ext.sql.SqlParameterManagerAware;
import org.elsfs.cloud.common.mybatis.ext.sql.TableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.TableAliasManagerAware;
import org.elsfs.cloud.common.mybatis.ext.sql.TokenSqlFragment;
import org.elsfs.cloud.common.mybatis.ext.sql.support.builder.StandardSelectSql;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.FieldEnumResolverSupport;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.HierarchyTableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.SimpleTableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.support.condition.ConditionItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.ExpressionSelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.StandardSelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.SubQuerySelectItem;
import org.elsfs.cloud.common.util.lang.ArrayUtils;
import org.elsfs.cloud.common.util.lang.CollectionUtils;
import org.elsfs.cloud.common.util.lang.StringUtils;

/** 查询Join构建器实现 */
public class WrapperSelectJoinBuilderImpl<
        E,
        R,
        J extends
            AbstractJoinWrapper<
                    E,
                    R,
                    J,
                    WrapperSelectJoinBuilderImpl<E, R, J>,
                    WrapperSelectJoinBuilderImpl<E, R, J>.WrapperJoinConditionBuilderImpl>>
    extends CompositeSqlFragment
    implements SelectJoinBuilder<
            WrapperSelectJoinBuilderImpl<E, R, J>,
            WrapperSelectJoinBuilderImpl<E, R, J>.WrapperJoinConditionBuilderImpl,
            J>,
        SqlParameterManagerAware,
        TableAliasManagerAware {

  /** 连接类型 */
  private final JoinType joinType;

  /** 连接表名称 */
  private final String table;

  /** 连接表别名 */
  private final String tableAlias;

  private final J joinQueryWrapper;

  /** Join条件构建器 */
  private WrapperJoinConditionBuilderImpl joinConditionBuilder;

  protected WrapperSelectJoinBuilderImpl(
      J joinQueryWrapper,
      JoinType joinType,
      String table,
      String tableAlias,
      boolean registerAlias) {
    this.joinQueryWrapper = joinQueryWrapper;
    this.joinType = joinType;
    this.table = table;

    this.tableAlias = tableAlias;
    if (registerAlias) {
      getTableAliasManager().registerAlias(table, this.tableAlias);
    }
  }

  protected WrapperSelectJoinBuilderImpl(
      J joinQueryWrapper, JoinType joinType, String table, String tableAlias) {
    this(joinQueryWrapper, joinType, table, tableAlias, true);
  }

  protected WrapperSelectJoinBuilderImpl(J joinQueryWrapper, JoinType joinType, String table) {
    this(
        joinQueryWrapper,
        joinType,
        table,
        joinQueryWrapper.getTableAliasManager().generateAlias(table),
        true);
  }

  /**
   * 解析字段枚举名称
   *
   * @param field 字段枚举
   * @return 字段名称
   */
  protected String resolveFieldName(Enum<?> field) {
    return FieldEnumResolverSupport.resolveFieldName(this.getTableAliasManager(), field);
  }

  /**
   * 返回上一级对象
   *
   * @return 上级对象
   */
  @Override
  public J end() {
    return joinQueryWrapper;
  }

  /**
   * 连接条件设置
   *
   * @return 连接条件构建器
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J>.WrapperJoinConditionBuilderImpl on() {
    this.joinConditionBuilder =
        new WrapperSelectJoinBuilderImpl<E, R, J>.WrapperJoinConditionBuilderImpl();
    return this.joinConditionBuilder;
  }

  /** 构建SQL之前处理 */
  @Override
  protected void beforeBuild() {
    this.addSqlFragment(new TokenSqlFragment(this.joinType.getJoinKeyword()));
    this.addSqlFragment(new TokenSqlFragment(this.table + " AS " + this.tableAlias));
    this.addSqlFragment(this.joinConditionBuilder);
  }

  /**
   * 获取SQL参数管理器
   *
   * @return SQL参数管理器
   */
  @Override
  public SqlParameterManager getSqlParameterManager() {
    return getSqlParameterManager();
  }

  /**
   * 选择关联SQL字段
   *
   * @param fields SQL字段
   * @return 具体实现
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J> select(String... fields) {
    if (ArrayUtils.isEmpty(fields)) {
      return this;
    }

    List<String> portionSelectedFields = new ArrayList<>(fields.length);
    for (String field : fields) {
      portionSelectedFields.add(this.buildSelectField(field));
    }

    select(portionSelectedFields);
    return this;
  }

  /**
   * 选择关联SQL字段
   *
   * @param fields SQL字段枚举
   * @return 具体实现
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J> select(Enum<?>... fields) {
    if (ArrayUtils.isEmpty(fields)) {
      return this;
    }

    List<String> portionSelectedFields = new ArrayList<>(fields.length);
    for (Enum<?> field : fields) {
      portionSelectedFields.add(this.buildSelectField(this.resolveFieldName(field)));
    }

    select(portionSelectedFields);
    return this;
  }

  /**
   * 选择关联SQL字段
   *
   * @param fields SQL字段
   * @return 具体实现
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J> select(Collection<String> fields) {
    List<String> portionSelectedFields = new ArrayList<>(fields.size());
    for (String field : fields) {
      portionSelectedFields.add(this.buildSelectField(field));
    }

    this.select(portionSelectedFields);
    return this;
  }

  /**
   * 选择关联SQL字段表达式
   *
   * @param expressions SQL字段表达式
   * @return 具体实现
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J> selectExpression(String... expressions) {
    if (ArrayUtils.isEmpty(expressions)) {
      return this;
    }

    List<String> portionSelectedExpressions = new ArrayList<>(expressions.length);
    for (String expression : expressions) {
      portionSelectedExpressions.add(this.buildSelectExpression(expression));
    }

    select(portionSelectedExpressions);
    return this;
  }

  /**
   * 选择关联SQL字段表达式
   *
   * @param expressions SQL字段表达式
   * @return 具体实现
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J> selectExpression(Collection<String> expressions) {
    if (CollectionUtils.isEmpty(expressions)) {
      return this;
    }

    List<String> portionSelectedExpressions = new ArrayList<>(expressions.size());
    for (String expression : expressions) {
      portionSelectedExpressions.add(this.buildSelectExpression(expression));
    }

    this.select(portionSelectedExpressions);
    return this;
  }

  /**
   * 选择SQL字段
   *
   * @param field SQL字段
   * @param fieldAlias 字段别名
   * @return 具体实现
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J> selectAs(String field, String fieldAlias) {
    if (StringUtils.isBlank(fieldAlias)) {
      throw new MybatisPlusException("选择字段[" + field + "]的别名不能为空");
    }

    return this.select(field + " AS " + fieldAlias);
  }

  /**
   * 选择SQL字段
   *
   * @param field SQL字段枚举
   * @param fieldAlias 字段别名
   * @return 具体实现
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J> selectAs(Enum<?> field, String fieldAlias) {
    return this.selectAs(this.resolveFieldName(field), fieldAlias);
  }

  /**
   * 选择SQL字段
   *
   * @param field SQL字段枚举
   * @param fieldAlias 字段别名枚举
   * @return 具体实现
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J> selectAs(Enum<?> field, Enum<?> fieldAlias) {
    return this.selectAs(field, fieldAlias.name());
  }

  /**
   * 选择子查询结果
   *
   * @param sqlBuilderConsumer SQL构建器消费器
   * @param fieldAlias 字段别名
   * @return 具体实现
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J> selectAs(
      Consumer<StandardSelectSql> sqlBuilderConsumer, String fieldAlias) {
    if (StringUtils.isBlank(fieldAlias)) {
      throw new MybatisPlusException("选择子查询的字段别名不能为空");
    }

    StandardSelectSql standardSelectSql =
        new StandardSelectSql(
            this.getSqlParameterManager(),
            new HierarchyTableAliasManager(
                new SimpleTableAliasManager(), this.getTableAliasManager()));
    sqlBuilderConsumer.accept(standardSelectSql);

    return this.select(
        new SubQuerySelectItem(standardSelectSql.buildSqlFragment(), fieldAlias)
            .buildSqlFragment());
  }

  /**
   * 选择子查询结果
   *
   * @param sqlBuilderConsumer SQL构建器消费器
   * @param fieldAlias 字段别名枚举
   * @return 具体实现
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J> selectAs(
      Consumer<StandardSelectSql> sqlBuilderConsumer, Enum<?> fieldAlias) {
    return this.selectAs(sqlBuilderConsumer, fieldAlias.name());
  }

  /**
   * 构建选择SQL字段
   *
   * @param field SQL字段
   * @return 选择SQL字段
   */
  private String buildSelectField(String field) {
    StandardSelectItem selectItem = new StandardSelectItem(field);
    if (StringUtils.isBlank(selectItem.getTableAlias())) {
      selectItem =
          new StandardSelectItem(
              this.tableAlias, selectItem.getField(), selectItem.getFieldAlias());
    }

    return selectItem.buildSqlFragment();
  }

  /**
   * 构建选择SQL字段表达式
   *
   * @param expression SQL字段表达式
   * @return 选择SQL字段表达式
   */
  private String buildSelectExpression(String expression) {
    return new ExpressionSelectItem(expression).buildSqlFragment();
  }

  /**
   * 选择关联SQL字段
   *
   * @param fields SQL字段枚举
   * @return 具体实现
   */
  @Override
  public WrapperSelectJoinBuilderImpl<E, R, J> selectEnum(Collection<Enum<?>> fields) {
    if (CollectionUtils.isEmpty(fields)) {
      return this;
    }

    List<String> portionSelectedFields = new ArrayList<>(fields.size());
    for (Enum<?> field : fields) {
      portionSelectedFields.add(this.buildSelectField(this.resolveFieldName(field)));
    }

    this.select(portionSelectedFields);
    return this;
  }

  /**
   * 获取表别名管理器
   *
   * @return 表名别管理器
   */
  @Override
  public TableAliasManager getTableAliasManager() {
    return joinQueryWrapper.getTableAliasManager();
  }

  /** Join条件构建器实现 */
  public class WrapperJoinConditionBuilderImpl
      extends AbstractCondition<
          WrapperSelectJoinBuilderImpl<E, R, J>.WrapperJoinConditionBuilderImpl>
      implements JoinConditionBuilder<
          WrapperSelectJoinBuilderImpl<E, R, J>.WrapperJoinConditionBuilderImpl,
          WrapperSelectJoinBuilderImpl<E, R, J>,
          J> {

    /** Join条件列表 */
    private final List<ConditionItem> conditionItems = Lists.newArrayList();

    /**
     * 获取SQL参数管理器
     *
     * @return SQL参数管理器
     */
    @Override
    public SqlParameterManager getSqlParameterManager() {
      return WrapperSelectJoinBuilderImpl.this.getSqlParameterManager();
    }

    /**
     * 添加一个条件
     *
     * @param conditionItem 条件
     */
    @Override
    public void addConditionItem(ConditionItem conditionItem) {
      this.conditionItems.add(conditionItem);
    }

    /**
     * 返回上一级对象
     *
     * @return 上级对象
     */
    @Override
    public J end() {
      return WrapperSelectJoinBuilderImpl.this.end();
    }

    /**
     * 构建SQL片段
     *
     * @return SQL片段
     */
    @Override
    public String buildSqlFragment() {
      if (this.conditionItems.isEmpty()) {
        return " ";
      }

      return String.format(
          "ON %s",
          ConditionItem.stripHeaderKeyword(
              this.conditionItems.stream()
                  .map(ConditionItem::buildSqlFragment)
                  .collect(Collectors.joining(" "))));
    }

    /**
     * 获取表别名管理器
     *
     * @return 表名别管理器
     */
    @Override
    public TableAliasManager getTableAliasManager() {
      return WrapperSelectJoinBuilderImpl.this.getTableAliasManager();
    }
  }
}
